/**
	Pulsar engine. Additional include file.
	Debug functions and asserts.

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.
		
	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

#if defined(PL_BREAK)
#undef PL_BREAK
#endif

#if defined(PL_ASSERTE)
#undef PL_ASSERTE
#endif

#if defined(PL_ASSERTW)
#undef PL_ASSERTW
#endif

#if defined(PL_TRACE)
#undef PL_TRACE
#endif

#if defined(PL_FATAL_ERROR)
#undef PL_FATAL_ERROR
#endif

#if defined(PL_STATIC_ASSERT)
#undef PL_STATIC_ASSERT
#endif

#if defined(PL_UNUSED_VARIABLE)
#undef PL_UNUSED_VARIABLE
#endif

namespace pl
{

/*
	Type of assertions
	Internal enum, use PL_ASSERTW()/PL_ASSERTE() macro
*/
enum AssertType
{
	ASSERT_TYPE_WARNING, // PL_ASSERTW
	ASSERT_TYPE_ERROR    // PL_ASSERTE
};

/*
	Assertion/other dialog boxes handlers behavior
*/
enum HandleResult
{
	HANDLE_RESULT_IGNORE,  // Ignore this error, continue execution
	HANDLE_RESULT_DEBUG,   // Break into debugger
	HANDLE_RESULT_NEXT     // Continue looking for event handler
};


/*
	Internal functions, use macro only
*/
extern HandleResult _callOnAssert(const pl_char* file, pl_uint32 line, const pl_char* func, const pl_char* cond, const pl_char* msg, pl::AssertType type);
extern HandleResult _callOnMemErr(const pl_char* file, pl_uint32 line, const pl_char* func, void* p, pl_int64 size, pl::MemoryError idEvent);
extern void _callOnTrace(const pl_char* file, pl_uint32 line, const pl_char* msg);

} // namespace pl

/*
	PL_UNUSED_VARIABLE() - macro to avoid compiler warning about unused arguments
*/
template <class T> inline void PL_UNUSED_VARIABLE(const T&) { }

/*
	PL_STATIC_ASSERT() - check compile-time expressions
*/
#define PL_STATIC_ASSERT(expr)  do \
                                { \
                                	typedef char static_assert_checker[ (expr) ? 1 : -1 ]; \
                                } while (false)

/*
	PL_FATAL_ERROR() - abnormal program ternination
*/
#define PL_FATAL_ERROR(errCode, msg)                                                   \
		do {                                                                           \
			pl::String<1024> str_;                                                     \
			str_ << msg;                                                               \
			pl::IAppEvents::callOnFatal(errCode, str_.get());                          \
		} while (false)

/**
	PL_BREAK() - break into debugger
	Works for PL_DEBUG and PL_RELEASE configurations only
*/
#if defined(PL_DEBUG) || defined(PL_RELEASE)

#if (defined(PL_INTEL) || defined(PL_INTEL64)) && defined(PL_MSC)

#define PL_BREAK()  do \
                    { \
                    	__debugbreak(); \
                    } while (0)

#elif defined(PL_INTEL) && defined(PL_GCC)

#define PL_BREAK() asm __volatile__("int3")

#elif defined(PL_LINUX)

#define PL_BREAK() *((int*)0) = 0

#else
#error "Wrong CPU type, compiler type or platform type"
#endif

#elif defined(PL_FINAL)

#define PL_BREAK()                ((void)0)

#else
#error "Wrong build configuration"
#endif

/**
	PL_ASSERTW/PL_ASSERTE - check preconditions
		PL_ASSERTW(bool condition, String text_message)
		PL_ASSERTE(bool condition, String text_message)

	PL_ASSERTW works for PL_DEBUG only
	PL_ASSERTE works for PL_DEBUG and PL_RELEASE
*/

#if defined(PL_DEBUG)

#define PL_ASSERTW(cond, msg)   if (!(cond) && pl::_callOnAssert(PL_T(__FILE__), (pl_uint32)__LINE__, PL_FUNCTION_NAME, PL_T(#cond), msg, pl::ASSERT_TYPE_WARNING) != pl::HANDLE_RESULT_IGNORE) PL_BREAK()

#elif defined(PL_RELEASE) || defined(PL_FINAL)

#define PL_ASSERTW(cond, msg)     ((void)0)

#else
#error "Wrong build configuration"
#endif

#if defined(PL_DEBUG) || defined(PL_RELEASE)

#define PL_ASSERTE(cond, msg)   if (!(cond) && !pl::_callOnAssert(PL_T(__FILE__), (pl_uint32)__LINE__, PL_FUNCTION_NAME, PL_T(#cond), msg, pl::ASSERT_TYPE_ERROR) != pl::HANDLE_RESULT_IGNORE) PL_BREAK()

#elif defined(PL_FINAL)

#define PL_ASSERTE(cond, msg)     ((void)0)

#else
#error "Wrong build configuration"
#endif

/*
	PL_TRACE() - fowrard message to debug window (OutputDebugString())
	Works for PL_DEBUG and PL_RELEASE configurations only
*/
#if defined(PL_DEBUG) || defined(PL_RELEASE)

#define PL_TRACE(msg)                                                 \
	do {                                                              \
	    pl::String<1024> str_;                                        \
	    str_ << msg;                                                  \
	    pl::_callOnTrace(PL_T(__FILE__), __LINE__, str_.get());       \
	}                                                                 \
	while(false)

#elif defined(PL_FINAL)

#define PL_TRACE(msg)     ((void)0)

#else
#error "Wrong build configuration"
#endif

//
// End of file 'pulsar_debugfunc.h'
//
